﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL, Software Libre	*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Net.Sockets;

using ESF.General;
using ESF.Peer.Handlers;
using System.Net;
using System.Collections.Generic;
using System.IO;
using ESF.Peer.Configuration;
using System.Configuration;
using System.Text;
using ESF.General.Entity;
using System.Timers;
using ESF.General.Network;

#endregion


namespace ESF.Peer
{
	#region Delegados
		
	public delegate void DelError (string pCode, string pMessage);
	
	public delegate void DelProcessedRequestObjects (string pEventCode, string pEventName, List<object> pProcessedRequestObjects);
	
	#endregion


	public abstract class BasePeer
	{
		#region Constantes
		
		//private const int MAXWAITMS = 10000;
		//private const int WAITMS = 200;
		public const int DEFAULT_SENDTIMEOUTMS = 20000;
		//private const int WAITMS = 20;
		//private const int MAXWAITMS = 10000;
		public const int DEFAULT_RECEIVETIMEOUTMS = 20000;
		private const int DEFAULT_MAXCONNECTIONS = 1000;
		
		#endregion


		#region Miembros
		
		private IForwarder _Forwarder;
		
		private IReceiver _Receiver;
		
		private string _Name;
		
		private IPEndPoint _MasterServerIPEndPoint;

		private List<Couple<IPEndPoint, int>> _ClientEndPoints;

		private List<IPAddress> _AllowedClients;

		private Timer _Timer;
		
		#endregion
		
		
		#region Propiedades
		
		public int CurrentClientCount { get { return _ClientEndPoints.Count; } }

		public IPEndPoint GetMasterServerIPEndPoint { get { return _MasterServerIPEndPoint; } }

		public string Name { get { return _Name; } }
		
		public IPEndPoint ReceiverIPEndPoint { get { return (_Receiver == null ? null : _Receiver.IPEndPoint); } }
		
		public int MaxFileReceiveSizeBytes { get { return _Receiver.MaxFileReceiveSizeBytes; } }

		public int MaxFileSendSizeBytes { get { return _Forwarder.MaxFileSendSizeBytes; } }

		#endregion
		
		
		#region Constructor
		
		private BasePeer () {}
		

		protected BasePeer (string pPeerInfoSectionName)
			: this (ConfigurationManager.GetSection (pPeerInfoSectionName) as PeerInfoSection) {}


		protected BasePeer (PeerInfoSection pPeerInfoSection) 
			: this (pPeerInfoSection.name, new IPEndPoint (IPAddress.Parse (pPeerInfoSection.IPAddress), pPeerInfoSection.port),
				pPeerInfoSection.maxConnections, pPeerInfoSection.noDelay, 
				pPeerInfoSection.sendTimeoutMS, pPeerInfoSection.receiveTimeoutMS,
				pPeerInfoSection.allowUseOtherPort, 
				(string.IsNullOrEmpty (pPeerInfoSection.MasterServerIP) ? IPAddress.None : IPAddress.Parse (pPeerInfoSection.MasterServerIP)),
				pPeerInfoSection.MasterServerPort, pPeerInfoSection.readBufferLength,
				pPeerInfoSection.allowedClients,
				pPeerInfoSection.receiveWaitTimeoutMS,
				pPeerInfoSection.maxFileSendSizeMB,
				pPeerInfoSection.maxFileReceiveSizeMB,
				pPeerInfoSection.timerInterval)
		{}

		
		protected BasePeer (string pName, IPEndPoint pReceiverIPEndPoint, 
			int pMaxConnections, bool pNoDelay, int pSendTimeoutMS, 
			int pReceiveTimeoutMS, bool pAllowUseOtherPort,
			IPAddress pMasterServerIP, int pMasterServerPort, 
			int pReadBufferLength, 
			string pAllowedClients, int pReceiveWaitTimeoutMS,
			int pMaxFileSendSizeMB, int pMaxFileReceiveSizeMB,
			int pTimerIntervalMs)
		{
			_Name = pName;
			

			_Timer = new Timer ();
			_Timer.Interval = pTimerIntervalMs;
			_Timer.Enabled = (pTimerIntervalMs > 0);
			_Timer.Elapsed += Timer_Elapsed;

			_Forwarder = new BasicForwarder ();
			_Forwarder.SendTimeoutMS = (pSendTimeoutMS > 0 ? pSendTimeoutMS : DEFAULT_SENDTIMEOUTMS);
			_Forwarder.NoDelay = pNoDelay;
			_Forwarder.OnEndSend += OnEndSend;
			_Forwarder.MaxFileSendSizeBytes = pMaxFileSendSizeMB * 1048576;

			_ClientEndPoints = new List<Couple<IPEndPoint,int>> ();
			 
			if (pMasterServerIP != IPAddress.None)
			{
				_MasterServerIPEndPoint = new IPEndPoint (pMasterServerIP, pMasterServerPort);
			}

			_Receiver = new BasicReceiver ();
			_Receiver.ReceiveTimeoutMS = (pReceiveTimeoutMS > 0 ? pReceiveTimeoutMS : DEFAULT_RECEIVETIMEOUTMS);
			_Receiver.IPEndPoint = TcpHelper.CheckAvailable (pReceiverIPEndPoint);
			_Receiver.MaxConnections = (pMaxConnections > 0 ? pMaxConnections : DEFAULT_MAXCONNECTIONS);
			_Receiver.OnClientConnection += OnStartReceive;
			_Receiver.OnReceive += OnReceive;
			_Receiver.ReadBufferLength = pReadBufferLength;
			_Receiver.ReceiveWaitTimeoutMS = pReceiveWaitTimeoutMS;
			_Receiver.OnConnectionUnused += OnConnectionUnused;
			_Receiver.OnStopForMaxReceiveSize += OnStopForMaxReceiveSize;
			_Receiver.OnClientDisconnected += OnClientDisconnected; 
			_Receiver.MaxFileReceiveSizeBytes = pMaxFileReceiveSizeMB * 1048576;
			

			_AllowedClients = new List<IPAddress> ();

			foreach (string allowedClient in TextHelper.Split (',', pAllowedClients))
			{
				if (NetworkHelper.VerifyIPorFQDN (allowedClient))
				{
					foreach (IPAddress ip in Dns.GetHostEntry (allowedClient).AddressList)
					{
						_AllowedClients.Add (ip);
					}
				}
				else
				{
					Log.Error (string.Format ("From the allowed clients {0} is invalid", allowedClient));
				}
			}
		}

		#endregion
		
		
		#region Funciones
		
		#region Inicio y Fin
		
		public void Stop ()
		{
			if (_Receiver != null)
			{
				Console.WriteLine (" - Stopping {0} Peer...", Name);
			
				_Receiver.Stop ();
			}
		}
		
		
		public void Start ()
		{
			if (_Receiver != null)
			{
				Console.WriteLine (" - Starting {0} Peer...", Name);
			
				_Receiver.Start ();
			}
		}
		
		#endregion

		#region Envio
		
		public void SendLine (Socket pSenderSocket, string pData)
		{
			_Forwarder.SendLine (pSenderSocket, pData);
		}


		public void Send (Socket pSenderSocket, string pData)
		{
			_Forwarder.Send (pSenderSocket, pData);
		}


		public void Send (Socket pSenderSocket, byte [] pData)
		{
			_Forwarder.Send (pSenderSocket, pData);
		}
		
		
		public void Send (IPEndPoint pTo, byte [] pData)
		{
			_Forwarder.Send (pTo, pData);
		}
		

		public void SendMasterServer (byte [] pData)
		{
			if (_MasterServerIPEndPoint != null)
			{
				Send (_MasterServerIPEndPoint, pData);
			}
		}

		#endregion
		
		
		protected virtual void OnStartReceive (Socket pRequestSocket)
		{
			IPAddress clientIPAddress;

			
			Console.WriteLine (" - Request from {0} was started... Clients {1}", pRequestSocket.RemoteEndPoint, CurrentClientCount);
			
			clientIPAddress = ((IPEndPoint) pRequestSocket.RemoteEndPoint).Address;

			if (!DataHelper.IsEmpty (_AllowedClients) 
				&& !_AllowedClients.Contains (clientIPAddress))
			{
				OnClientDenied (pRequestSocket);
				
				if (pRequestSocket.Connected)
				{
					pRequestSocket.Close ();

					Log.WriteHistory (string.Format ("Client from {0} try to connect but it is not in the allowed list", clientIPAddress));

					return;
				}
			}

			lock (_ClientEndPoints)
			{
				// Registra el cliente y la marca de tiempo relativa a su ultima interaccion (la conexion)
				if (!_ClientEndPoints.Exists (ce => ce.Key == (IPEndPoint) pRequestSocket.RemoteEndPoint))
				{
					OnClientConnection (pRequestSocket);

					_ClientEndPoints.Add (
						new Couple<IPEndPoint, int> () { 
							Key = (IPEndPoint) pRequestSocket.RemoteEndPoint, 
							Value = Environment.TickCount });
				}
			}
		}

		#endregion 
		
		
		#region Funciones para implementar

		protected virtual void OnReceive (Socket pRequestSocket, Stream pData) {}

		protected virtual void OnReceiveString (Socket pRequestSocket, string pData) {}

		protected virtual void OnClientConnection (Socket pRequestSocket) {}

		protected virtual void OnEndSend (Socket pResponseSocket) {}

		protected virtual void OnClientDisconnected(IPEndPoint pClientIPEndPoint, Exception pEx) 
		{
			_ClientEndPoints.RemoveAll (ce => ce.Key == pClientIPEndPoint); 
		}

		protected virtual void OnClientDenied (Socket pRequestSocket) {}

		protected virtual void OnConnectionUnused (Socket pRequestSocket) {}

		protected virtual void OnStopForMaxReceiveSize (Socket pRequestSocket) {}

		#endregion


		#region Timer Functions

		protected virtual void Timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			
		}

		#endregion
	}
}
